package com.zx.security.reactive.service.impl;


import com.zx.core.constant.TokenConstants;
import com.zx.redis.reactive.service.RedisReactiveService;
import com.zx.security.common.model.LoginUser;
import com.zx.security.reactive.service.ICustomTokenService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

/**
 * <p>
 * description: token验证处理实现 <br>
 * create: 2025-05-06 10:50
 * </p>
 *
 * @author zhou  xun
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CustomTokenServiceImpl implements ICustomTokenService {
    private final RedisReactiveService redisReactiveService;

    private final ReactiveJwtDecoder reactiveJwtDecoder;

    /**
     * 获取用户身份信息
     *
     * @param token 令牌
     * @return {@link Mono <LoginUser>}
     * @author zhou  xun
     * @since 2025-05-13
     */
    @Override
    public Mono<LoginUser> getLoginUser(String token) {
        if (!StringUtils.hasLength(token)) {
            return Mono.empty();
        }

        return reactiveJwtDecoder.decode(token)
                .switchIfEmpty(Mono.error(new RuntimeException("JWT 解码失败：无效或格式错误的 token")))
                .flatMap(jwt -> {
                    String userId = jwt.getClaimAsString("userId");
                    if (!StringUtils.hasLength(userId)) {
                        return Mono.error(new IllegalArgumentException("JWT 缺少 userId 声明"));
                    }

                    String redisKey = TokenConstants.REDIS_LOGIN_KEY_PREFIX + userId;
                    return redisReactiveService.getCachedObjectReactive(redisKey, LoginUser.class)
                            .switchIfEmpty(Mono.error(new RuntimeException("用户未登录或token已过期")))
                            .onErrorResume(e -> {
                                log.error("从Redis获取用户信息失败，键为: {}", redisKey, e);
                                return Mono.error(new RuntimeException("用户信息获取失败"));
                            });
                })
                .onErrorResume(e -> {
                    log.error("JWT解码失败", e);
                    return Mono.error(new RuntimeException("令牌验证失败"));
                });
    }

    /**
     * 删除用户身份信息
     *
     * @param token 令牌
     * @return {@link Mono<Void>}
     * @author zhou  xun
     * @since 2025-05-13
     */
    @Override
    public Mono<Void> delLoginUser(String token) {
        if (!StringUtils.hasLength(token)) {
            return Mono.error(new IllegalArgumentException("令牌为空"));
        }

        return reactiveJwtDecoder.decode(token)
                .switchIfEmpty(Mono.error(new RuntimeException("用户未登录或token已过期")))
                .flatMap(jwt -> {
                    String userId = jwt.getClaimAsString("userId");
                    if (!StringUtils.hasLength(userId)) {
                        return Mono.error(new IllegalArgumentException("无效的用户标识"));
                    }

                    String redisKey = TokenConstants.REDIS_LOGIN_KEY_PREFIX + userId;
                    return redisReactiveService.deleteObject(redisKey)
                            .then()
                            .onErrorResume(e -> {
                                log.error("删除用户缓存失败: {}", e.getMessage());
                                return Mono.error(new RuntimeException("用户注销失败"));
                            });
                })
                .onErrorResume(e -> {
                    log.error("注销时JWT解码失败", e);
                    // 注销失败时静默处理
                    return Mono.empty();
                });

    }

    /**
     * 获取响应式JWT解码器
     *
     * @return {@link ReactiveJwtDecoder}
     */
    @Override
    public ReactiveJwtDecoder getReactiveJwtDecoder() {
        return reactiveJwtDecoder;
    }
}